#pragma once

#include <iostream>
#include <string>
#include <sstream>
#include <fstream>
#include <fcntl.h>
#include <unistd.h>
#include <locale.h>
#include "http_protocol.hpp"

#include <vector>
#include <memory>
#include <cstring>
//#include <filesystem>
#include<experimental/filesystem>  // 编译时须链接动态库 -lstdc++fs
#include <sys/stat.h>
#include <jsoncpp/json/json.h>
#include "bundle.h"
#include "logs/log.h"

namespace cloud
{
    class util
    {
    public:
        static void set_non_block(int fd)
        {
            // INFO("设置非阻塞，fd：%d", fd);
            int fl = fcntl(fd, F_GETFD);
            fcntl(fd, F_SETFL, O_NONBLOCK); // 第二个参数为F_SETFL
        }

        static std::string read_one_line(std::string &str, std::string &&sep)
        {
            // GET / HTTP/1.1
            size_t pos = str.find(sep);
            if(pos == std::string::npos)
            {
                ERROR("未找到分隔符号:%s", sep.c_str());
                return "";
            }
            
            std::string one_line = str.substr(0, pos);
            str.erase(0, pos + sep.size());
            return one_line;
        }

        static void parse_firse_line(std::string &line, Request &req)
        {
            // GET /abc/x y z.txt HTTP/1.1
            // std::stringstream ss(line);
            // ss >> req._method >> req._url >> req._version;
            size_t start = line.find_first_of(" ");
            size_t end = line.find_last_of(" ");
            req._method = line.substr(0, start);
            req._url = line.substr(start + 1, end - start - 1);
            req._version = line.substr(end + 1);
        }

        // 从str中读取一个http请求到req中
        // 反序列化
        static Request fill_request(std::string str)
        {
            Request req;

            // 读取第一行
            std::string line = read_one_line(str, "\r\n");
            parse_firse_line(line, req);

            // 读取报头字段
            line = read_one_line(str, "\r\n");
            while(line.size() != 0)
            {
                req._heads.push_back(line);
                if(req._content_len == 0)
                    check_req(line, req);
                line = read_one_line(str, "\r\n");
            }

            // 读取正文
            req._content = str;       

            return req;
        }

        // 序列化，将Response转为string
        static std::string serialize(Response &res)
        {
            std::string str;
            str += res._version;
            str += " ";
            str += res._status;
            str += " ";
            str += res._info;
            str += "\r\n";

            for(auto &head : res._heads)
            {
                str += head;
                str += "\r\n";
            }

            str += "\r\n";
            str += res._content;
            return str;
        }

        static std::string read_file(const std::string &path)
        {
            std::ifstream ifs;
            ifs.open(path, std::ios::binary);
            if(ifs.is_open() == false)
            {
                // std::cout << "read_file 文件打开失败" << std::endl;
                ERROR("文件打开失败 --> %s", path.c_str());
                return "";
            }

            ifs.seekg(0, std::ios::end);
            size_t fsize = ifs.tellg();
            ifs.seekg(0, std::ios::beg);
            std::string file;
            file.resize(fsize);
            ifs.read(&file[0], fsize);
            if(ifs.good() == false)
            {
                ERROR("读取文件失败 --> %s", path.c_str());
                ifs.close();
                return "";
            }
            ifs.close();

            // std::cout << "文件读取成功" << std::endl;
            INFO("文件读取成功 --> %s", path.c_str());
            return file;
        }

        // 检查某些特定的http报头字段
        static void check_req(std::string &line, Request &req)
        {
            if (line.find("Content-Length") != std::string::npos)
            {
                size_t pos = line.find(" ");
                std::string nums = line.substr(pos + 1);
                req._content_len = atoi(nums.c_str());
                // INFO("Content-Length为%d", req._content_len);
            }
            if (line.find("boundary") != std::string::npos)
            {
                size_t pos = line.find("boundary");
                req._boundary = line.substr(pos + 9);
            }
        }

        // 时间戳转字符串
        static std::string t_to_s(time_t t)
        {
            struct tm ts;
            localtime_r(&t, &ts);
            std::stringstream ss;
            ss << ts.tm_year + 1900 << "/";
            ss << ts.tm_mon + 1 << "/";
            ss << ts.tm_mday;
            ss << " ";
            ss << ts.tm_hour << ":";
            ss << ts.tm_min << ":";
            ss << ts.tm_sec;
            return ss.str();
        }

        static std::string code_convert(const std::string& str)
        {
            // url为%E6%B1%89%E5%AD%97%20%E6%B5%8B%E8%AF%95.txt时
            // 这是url上为了传递汉字使用的方法，为ascii编码表示汉字的方式，须进行解码
            // std::string res;
            // for(size_t i = 0; i < str.size(); ++i)
            // {
            //     if(str[i] == '%' && i + 2 < str.size()
            //     && (str[i + 1] >= '0' && str[i + 1] <= '9') || (str[i + 1] >= 'A' && str[i + 1] <= 'F')
            //     && (str[i + 2] >= '0' && str[i + 2] <= '9') || (str[i + 2] >= 'A' && str[i + 2] <= 'F'))
            //     {
            //         uint64_t ic = *((uint64_t*)(&str[i + 1]));
            //         char c = static_cast<char>(ic);
            //         res += c;
            //         i += 2;
            //     }
            //     else
            //     {
            //         res += str[i];
            //     }
            // }

            std::string res;
            char ch;
            int ii;
            for (size_t i = 0; i < str.length(); i++)
            {
                //if (int(str[i]) == 37)
                if(str[i] == '%' && i + 2 < str.size()
                && (str[i + 1] >= '0' && str[i + 1] <= '9') || (str[i + 1] >= 'A' && str[i + 1] <= 'F')
                && (str[i + 2] >= '0' && str[i + 2] <= '9') || (str[i + 2] >= 'A' && str[i + 2] <= 'F'))
                {
                    // %x表示无符号十六进制整数
                    sscanf(str.substr(i + 1, 2).c_str(), "%x", &ii);
                    ch = static_cast<char>(ii);
                    res += ch;
                    i = i + 2;
                }
                else
                {
                    res += str[i];
                }
            }

            // INFO("解码前：%s", str.c_str());
            // INFO("解码后：%s", res.c_str());
            return res;
        }

        static void get_req_test(const std::string& str)
        {
            std::ofstream ofs("汉字测试请求样例.txt", std::ios::binary);
            ofs.write(str.c_str(), str.size());
            ofs.close();
            INFO("汉字测试请求样例生成成功");
        }
    };


    namespace fs = std::experimental::filesystem;
    class file_util
    {
    public:
        file_util(const std::string &file_name):_file_name(file_name){}
        ~file_util(){}

        int64_t file_size()
        {
            struct stat s;
            int n = stat(&_file_name[0], &s);
            if(n < 0)
            {
                // std::cout << "file_size fail" << std::endl;
                ERROR("文件不存在");
                return -1;
            }
            return s.st_size;
        }

        // 最后一次修改时间
        time_t last_modify_time()
        {
            struct stat s;
            int n = stat(&_file_name[0], &s);
            if(n < 0)
            {
                // std::cout << "last_modify_time fail" << std::endl;
                ERROR("文件不存在");
                return -1;
            }
            return s.st_mtime;
        }

        // 最后一次访问时间
        time_t last_access_time()
        {
            struct stat s;
            int n = stat(&_file_name[0], &s);
            if(n < 0)
            {
                // std::cout << "last_access_time fail" << std::endl;
                ERROR("文件不存在");
                return -1;
            }
            return s.st_atime;
        }

        // 根据路径，返回不带路径的文件名
        std::string file_name()
        {
            // ./x/y/z/test.txt
            size_t pos = _file_name.find_last_of("/\\");
            if(pos == std::string::npos)
            {
                return _file_name;
            }
            return _file_name.substr(pos + 1);
        }

        // 指定位置，指定长度，从_file_name读取数据到str
        bool get_pos_len(std::string &str, int64_t pos, int64_t len)
        {
            if(pos + len > file_size())
            {
                // std::cout << "get_pos_len out len" << std::endl;
                ERROR("超出文件范围");
                return false;
            }
            std::ifstream ifs;
            ifs.open(_file_name, std::ios::binary);
            if(ifs.is_open() == false)
            {
                // std::cout << "get_pos_len open file fail" << std::endl;
                ERROR("文件打开失败");
                return false;
            }
            ifs.seekg(pos, std::ios::beg);
            str.resize(len);
            ifs.read(&str[0], len);
            if(ifs.good() == false)
            {
                // std::cout << "get_pos_len read file fail" << std::endl;
                ERROR("读取文件失败");
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }

        // 从_file_name读取数据到str
        bool get_content(std::string &str)
        {
            return get_pos_len(str, 0, file_size());
        }

        // 将str中的数据写入_file_name
        bool set_content(std::string &str)
        {
            std::ofstream ofs;
            ofs.open(&_file_name[0], std::ios::binary);
            if(ofs.is_open() == false)
            {
                // std::cout << "set_content open file fail" << std::endl;
                ERROR("文件打开失败");
            }
            ofs.write(&str[0], str.size());
            if(ofs.good() == false)
            {
                // std::cout << "set_content write file fail" << std::endl;
                ERROR("写入文件失败");
                ofs.close();
                return false;
            }
            ofs.close();
            return true;
        }

        bool file_remove()
        {
            return remove(_file_name.c_str()) == 0;
        }

        // 压缩，传入压缩后的文件名字
        bool compress(const std::string &packed_name)
        {
            std::string file;
            if(get_content(file) == false)
            {
                std::cout << "compress get file fail" << std::endl;
                return false;
            }
            std::string packed = bundle::pack(bundle::LZMA20, file);
            file_util fu(packed_name);
            if(fu.set_content(packed) == false)
            {
                std::cout << "compress set file fail" << std::endl;
                return false;
            }
            return true;
        }

        // 解压缩，传入解压后的文件名字
        bool decompress(const std::string &unpack_name)
        {
            std::string file;
            if(get_content(file) == false)
            {
                std::cout << "decompress get file fail" << std::endl;
                return false;
            }
            std::string unpacked = bundle::unpack(file);
            file_util fu(unpack_name);
            if(fu.set_content(unpacked) == false)
            {
                std::cout << "decompress set file fail" << std::endl;
                return false;
            }
            return true;
        }

        bool exist()
        {
            // struct stat s;
            // return stat(_file_name.c_str(), &s) == 0;

            // C++17
            return fs::exists(_file_name);
        }

        bool create_dir()
        {
            // C++17
            return fs::create_directories(_file_name);
        }

        // 遍历目录
        bool scan_dir(std::vector<std::string> &v)
        {
            // C++17
            // 这玩意不能遍历目录中的目录 中的文件
            for(auto &it : fs::directory_iterator(_file_name))
            {
                if(fs::is_directory(it) == true)//跳过目录文件
                {
                    continue;
                }
                // relative_path() --> 返回相对根路径的路径
                v.push_back(fs::path(it).relative_path().string());
            }
            return true;
        }
    private:
        std::string _file_name; // 文件名，但也可能是路径
    };

    class json_util
    {
    public:
        static void serialize(const Json::Value &root, std::string &str)
        {
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
            std::stringstream ss;
            sw->write(root, &ss);
            str = ss.str();
        }

        static void deserialize(Json::Value &root, std::string &str)
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
            std::string err;
            bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), &root, &err);
            if (ret == false)
            {
                ERROR("字符串解析失败:%s", err.c_str());
            }
        }
    };
}